package work.xiaohong.search.controller;

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import work.xiaohong.search.config.ElasticsearchConfig;
import work.xiaohong.search.entity.SearchQuery;
import work.xiaohong.search.entity.SearchRespVo;
import work.xiaohong.search.entity.SkuEsModel;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 将DSL转换为代码
 */
@RequestMapping("/dsl")
@RestController
public class PracticeSearch {


    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @GetMapping("/product")
    public SearchRespVo productSearch(SearchQuery searchQuery){

        // 返回的结果
        SearchRespVo result = null;

        // 准备检索请求
        SearchRequest searchRequest = buildSearchRequest(searchQuery);

        try {
            SearchResponse response = restHighLevelClient.search(searchRequest, ElasticsearchConfig.COMMON_OPTIONS);
            //
            result = buildSearchResult(response,searchQuery);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;

    }


    /**
     * 构建搜索请求
     * @param searchQuery
     * @return
     */
    public SearchRequest buildSearchRequest(SearchQuery searchQuery){
        // 构建DSL语句
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        /**
         * 查询 模糊匹配，过滤（按照属性，分类，品牌，价格区间，库存）
         */
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        if (StringUtils.isNotEmpty(searchQuery.getKeyword())){
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle",searchQuery.getKeyword()));
        }


        if (searchQuery.getCatelog3Id() != null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("catelogId",searchQuery.getCatelog3Id()));
        }

        if (searchQuery.getBrandId() != null && searchQuery.getBrandId().size() > 0){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",searchQuery.getBrandId()));
        }

        // 构造属性参数查询
        if (searchQuery.getAttrs() != null && searchQuery.getAttrs().size() > 0){

            // &attrs=15_海思：以官网信息为准&attrs=22_金色:黑色：蓝色
            for (String attr : searchQuery.getAttrs()) {
                BoolQueryBuilder boolQueryBuilderNested = QueryBuilders.boolQuery();

                //attrs=15_海思：以官网信息为准
                // 获取属性id
                String[] s = attr.split("_");
                String attrId = s[0];
                // 获取检索的属性值
                String[] attrValues = s[1].split(":");
                boolQueryBuilderNested.must(QueryBuilders.termQuery("attrs.attrId",attrId));
                boolQueryBuilderNested.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));

                // 每一个属性都得存在一个nested查询
                // 第三个参数表示聚合的结果以什么样的方式计入得分 None表示不计入得分
                NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs", boolQueryBuilderNested, ScoreMode.None);
                boolQueryBuilder.filter(nestedQueryBuilder);
            }

        }


        // 是否有库存 1有库存 0无库存
        boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock",searchQuery.getHasStock() == 1));

        // 价格区间 1_500/_500/500_
        if (StringUtils.isNotEmpty(searchQuery.getSkuPrice())){
            String skuPrice = searchQuery.getSkuPrice();
            String[] s = skuPrice.split("_");
            if (s.length == 2){
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gte(s[0]).lte(s[1]));
            }else if (s.length == 1) {
                if (skuPrice.startsWith("_")){
                    boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").lte(s[0]));
                }else {
                    boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gte(s[0]));
                }
            }else {
                // 错误数据
            }
        }

        searchSourceBuilder.query(boolQueryBuilder);

        /**
         * 排序，分页，高亮
         */

        /**
         * 排序
         *  sort = saleCount_ase/desc
         *  sort = skuPrice_ase/desc
         *  sort = hostScore_ase/desc
         */
        if (StringUtils.isNotEmpty(searchQuery.getSort())){
            String sort = searchQuery.getSort();
            String[] s = sort.split("_");
            SortOrder order = "asc".equalsIgnoreCase(s[1]) ? SortOrder.ASC : SortOrder.DESC;
            searchSourceBuilder.sort(s[0],order);
        }

        /**
         * 分页 pageSiz: 5
         * pageNum:1 from 0
         * pageNum:2 from 5
         * from = (pageNum-1)*size
         */
        searchSourceBuilder.from((searchQuery.getPageNum()-1)*5);
        // 每页固定5条数据
        searchSourceBuilder.size(5);

        // 高亮
        if (StringUtils.isNotEmpty(searchQuery.getKeyword())){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            searchSourceBuilder.highlighter(highlightBuilder);
        }

        /**
         * 聚合分析
         */
        // 品牌聚合
        TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg").field("brandId").size(50);

        // 品牌子聚合 一个品牌对应一个品牌名字和图片，所以size为1
        brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
        brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
        searchSourceBuilder.aggregation(brand_agg);

        // 分类聚合
        TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(20);

        // 分类子聚合 一个分类对应一个name 所以size为1
        catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
        searchSourceBuilder.aggregation(catalog_agg);

        // 属性聚合
        NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
        // 聚合当前所有的attrId
        TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");
        // 聚合当前所有的attrId对应的名字
        attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
        // 聚合当前所有的attrId对应的所有可能的属性值attrValue
        attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));

        attr_agg.subAggregation(attr_id_agg);
        searchSourceBuilder.aggregation(attr_agg);

        String toString = searchSourceBuilder.toString();
        System.out.println("构建的DSL" + toString);

        SearchRequest searchRequest = new SearchRequest(new String[]{"product"}, searchSourceBuilder);
        return searchRequest;

    }

    /**
     * 分析响应数据封装成想要的结果
     * @param response
     * @return
     */
    private SearchRespVo buildSearchResult(SearchResponse response,SearchQuery searchQuery) {
        SearchRespVo searchRespVo = new SearchRespVo();
        SearchHits searchHits = response.getHits();
        /**
         * 封装分页信息
         */
        Long total = searchHits.getTotalHits().value;
        searchRespVo.setTotal(total);
        Integer totalPage = Math.toIntExact((total % 5 == 0) ? (total / 5) : (total / 5 + 1));
        searchRespVo.setTotalPage(totalPage);
        searchRespVo.setPageNum(searchQuery.getPageNum());
        List<Integer> pageNavs = new ArrayList<>();
        for (Integer i = 1; i <= totalPage; i++) {
            pageNavs.add(i);
        }
        searchRespVo.setPageNavs(pageNavs);
        /**
         * 封装skuModel
         */
        SearchHit[] hits = searchHits.getHits();
        List<SkuEsModel> skuEsModelList = new ArrayList<>();
        if (hits != null && hits.length > 0) {
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                SkuEsModel skuEsModel = JSON.parseObject(sourceAsString, SkuEsModel.class);
                if (!StringUtils.isEmpty(searchQuery.getKeyword())) {
                    // 显示高亮 TODO 有问题，highlightFields为null
                    Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                    HighlightField highlight = highlightFields.get("skuTitle");
                    if (highlight != null) {
                        Text[] fragments = highlight.fragments();
                        String fragmentString = fragments[0].string();
                        System.out.println(fragmentString);
                        skuEsModel.setSkuTitle(fragmentString);
                    }
                }
                skuEsModelList.add(skuEsModel);
            }
        }
        searchRespVo.setProducts(skuEsModelList);
        /**
         * 封装 分类聚合信息
         */
        ParsedLongTerms catelog_agg = response.getAggregations().get("catelog_agg");
        List<SearchRespVo.CatelogVo> catelogVos = new ArrayList<>();
        for (Terms.Bucket bucket : catelog_agg.getBuckets()) {
            SearchRespVo.CatelogVo catelogVo = new SearchRespVo.CatelogVo();
            String idString = bucket.getKeyAsString();
            catelogVo.setCatelogId(Long.parseLong(idString));
            ParsedStringTerms catelog_name_agg = bucket.getAggregations().get("catelog_name_agg");
            String nameString = catelog_name_agg.getBuckets().get(0).getKeyAsString();
            catelogVo.setCatelogName(nameString);
            catelogVos.add(catelogVo);
        }
        searchRespVo.setCatelogVos(catelogVos);
        /**
         * 封装 品牌聚合信息
         */
        List<SearchRespVo.BrandVo> brandVos = new ArrayList<>();
        ParsedLongTerms brand_agg = response.getAggregations().get("brand_agg");
        for (Terms.Bucket bucket : brand_agg.getBuckets()) {
            SearchRespVo.BrandVo brandVo = new SearchRespVo.BrandVo();
            brandVo.setBrandId((Long) bucket.getKey());
            ParsedStringTerms brand_img_agg = bucket.getAggregations().get("brand_img_agg");
            brandVo.setBrandImg(brand_img_agg.getBuckets().get(0).getKeyAsString());
            ParsedStringTerms brand_name_agg = bucket.getAggregations().get("brand_name_agg");
            brandVo.setBrandName(brand_name_agg.getBuckets().get(0).getKeyAsString());
            brandVos.add(brandVo);
        }
        searchRespVo.setBrands(brandVos);
        /**
         *  封装attrs
         */
        List<SearchRespVo.AttrVo> attrVos = new ArrayList<>();
        ParsedNested attr_agg = response.getAggregations().get("attr_agg");
        ParsedLongTerms attr_id_agg = attr_agg.getAggregations().get("attr_id_agg");
        for (Terms.Bucket bucket : attr_id_agg.getBuckets()) {
            SearchRespVo.AttrVo attrVo = new SearchRespVo.AttrVo();
            Long attrId = (Long) bucket.getKey();
            attrVo.setAttrId((attrId));
            ParsedStringTerms attr_name_agg = bucket.getAggregations().get("attr_name_agg");
            attrVo.setAttrName(attr_name_agg.getBuckets().get(0).getKeyAsString());
            ParsedStringTerms attr_value_agg = bucket.getAggregations().get("attr_value_agg");
            List<String> attrValues = new ArrayList<>();
            for (int i = 0; i < attr_value_agg.getBuckets().size(); i++) {
                attrValues.add(attr_value_agg.getBuckets().get(i).getKeyAsString());
            }
            attrVo.setAttrValue(attrValues);
            attrVos.add(attrVo);
        }
        searchRespVo.setAttrs(attrVos);
        /**
         * 构建面包屑导航
         * 属性面包屑导航
         */
        List<String> attrs = searchQuery.getAttrs();
        List<SearchRespVo.NavVo> navVoList = new ArrayList<>();
        if (attrs != null && attrs.size() > 0) {
            navVoList = attrs.stream().map(item -> {
                SearchRespVo.NavVo navVo = new SearchRespVo.NavVo();
                String[] s = item.split("_");
                // 远程查出attrName
//                R r = productFeignService.info(Long.parseLong(s[0]));
//
//                searchRespVo.getAttrIds().add(Long.parseLong(s[0]));
//                if (r.getCode() == 0) {
//                    AttrResponseVo attrResponseVo = r.getData("attr", new TypeReference<AttrResponseVo>() {
//                    });
//                    navVo.setNavName(attrResponseVo.getAttrName());
//                } else {
//                    navVo.setNavName("");
//                }
//                String nQueryString = replaceUrl(searchParam, item, "attrs");
//                navVo.setLink("http://search.gulimall.com/list.html?" + nQueryString);
                navVo.setNavValue(s[1]);
                return navVo;
            }).collect(Collectors.toList());
        }
        searchRespVo.setNavs(navVoList);
        /**
         * 品牌面包屑导航
         */
        if (searchQuery.getBrandId() != null && searchQuery.getBrandId().size() > 0) {
            navVoList = searchRespVo.getNavs();
            SearchRespVo.NavVo navVo = new SearchRespVo.NavVo();
            // 远程查询品牌
//            R r = productFeignService.getBrandByIds(searchParam.getBrandId());
//            if (r.getCode() == 0) {
//                List<BrandVo> brands = r.getData("brands", new TypeReference<List<BrandVo>>() {
//                });
//                StringBuffer buffer = new StringBuffer();
//                String replace = "";
//                for (int i = 0; i < brands.size(); i++) {
//                    BrandVo brandVo = brands.get(i);
//                    if (i == 0) {
//                        buffer.append(brandVo.getName());
//                    } else {
//                        buffer.append(brandVo.getName() + ";");
//                    }
//                    replace = replaceUrl(searchParam, brandVo.getBrandId() + "", "brandId");
//                }
//                navVo.setNavName("");
//                navVo.setNavValue(buffer.toString());
//                navVo.setLink("http://search.gulimall.com/list.html?" + replace);
//            }
            navVoList.add(navVo);
            searchRespVo.setNavs(navVoList);
        }
        /**
         * 分类面包屑导航
         */
        if (searchQuery.getCatelog3Id() != null) {
            SearchRespVo.NavVo navVo = new SearchRespVo.NavVo();
            // 远程查询分类的值
//            R r = productFeignService.getCateInfo(searchParam.getCatelog3Id());
//            if (r.getCode() == 0) {
//                CateVo category = r.getData("category", new TypeReference<CateVo>() {
//                });
//                navVo.setNavName("");
//                navVo.setNavValue(category.getName());
//            }
            if (navVo != null) {
                navVoList.add(navVo);
            }
            searchRespVo.setNavs(navVoList);
        }
        return searchRespVo;
    }


    /**
     * 构建特换url
     *
     * @param item 字段value
     * @param key  字段名称
     * @return
     */
    private String replaceUrl(SearchQuery searchQuery, String item, String key) {
        String encode = "";
        try {
            encode = URLEncoder.encode(item, "utf-8");
            encode = encode.replace("+", "%20");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
//        return searchQuery.get_queryString().replace("&" + key + "=" + encode, "");
        return searchQuery.getKeyword().replace("&" + key + "=" + encode, "");
    }



}
